#import "SearchesView.h"

#import "SearchesController.h"
#import "SearchSourceListElement.h"
#import "Searches.h"
#import "ApplicationDefaults.h"
#import "FriendsSourceListElement.h"
#import "ChatElements.h"
#import "Chats.h"
#import "ChatsController.h"
#import "BrowseElement.h"

@interface SearchesView (Private)
- (void)addSearches:(NSArray *)new;
- (BrowseElement *)browsePeer:(Peer *)peer select:(BOOL)select saveImmediately:(BOOL)save; 
- (void)removeSearches:(NSArray *)old;
- (SourceListElement *) elementForSearch:(Search *)search;
@end


@interface SearchesElement : SourceListArrayElement {}
@end

@implementation SearchesElement
- (NSArray *)searches { 
	return [[[NSApp engine] searchesController] searches];
}

- (void)setSelected:(BOOL)selected {
	if(selected==NO) {
		[(SearchesView *)[self view] clear:self];
	}
}

- (NSString *)name { return @"Searches"; }
- (NSImage *)icon { return [NSImage imageNamed:@"sourcelistSearch"]; }

@end


@implementation SearchesView

- (id)initWithFrame:(NSRect)frameRect
{
	if ((self = [super initWithFrame:frameRect]) != nil) {
		_elementsSearch = [[SearchesElement alloc] initWithView:self];
		_dictionaryElementsSearch = [[NSMutableDictionary alloc] init];
		_dictionaryElementsBrowse = [[NSMutableDictionary alloc] init];
	}
	return self;
}

- (void)dealloc {
	[NSApp removeObserver:self forKeyPath:@"engine.searchesController.searches"];
	[_elementsSearch release];
	[_dictionaryElementsSearch release];
	[_dictionaryElementsBrowse release];
	[super dealloc];
}

- (void)awakeFromNib {
		[NSApp addObserver:self forKeyPath:@"engine.searchesController.searches" 
				options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
		[self addSearches:[[[NSApp engine] searchesController] valueForKey:@"searches"]];
		[self performSelector:@selector(autoloadSearches) withObject:nil afterDelay:0.5];
		[tableSearches setTarget:self];
		[tableSearches setDoubleAction:@selector(viewResults:)];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
	NSAssert2(object==NSApp && [keyPath isEqual:@"engine.searchesController.searches"], @"Should not observe %@ for %@.",keyPath,object);
	int kind = [[change objectForKey:NSKeyValueChangeKindKey] intValue];
	
	NSArray *old = [change objectForKey:NSKeyValueChangeOldKey];		
	NSArray *new = [change objectForKey:NSKeyValueChangeNewKey];
	
	switch(kind) {
		NSKeyValueChangeRemoval:
		NSKeyValueChangeInsertion:
		NSKeyValueChangeReplacement: {
			[_elementsSearch willChange:[[change objectForKey:NSKeyValueChangeKindKey] intValue] valuesAtIndexes:[change objectForKey:NSKeyValueChangeIndexesKey] forKey:@"searches"];
			[_elementsSearch didChange:[[change objectForKey:NSKeyValueChangeKindKey] intValue] valuesAtIndexes:[change objectForKey:NSKeyValueChangeIndexesKey] forKey:@"searches"];

			if([old count]) 
				[self removeSearches:old];
			if([new count])
				[self addSearches:new];
			break;
		}
		default:
			[_elementsSearch willChangeValueForKey:@"searches"];
			[_elementsSearch didChangeValueForKey:@"searches"];
			
			if([[NSNull null] isEqual:old]==NO)
				[self removeSearches:old];
			if([[NSNull null] isEqual:new]==NO)
				[self addSearches:new];
	}
		
	[NSApp updateSourceList];
}

#pragma mark KVO/C

- (id<SourceListElement>)element { return _elementsSearch; }


#pragma mark actions


- (void)addSearches:(NSArray *)new {
	if([new count]) {
		NSMutableArray *add = [NSMutableArray arrayWithCapacity:[new count]];
		//fejta: TODO: medium: research objectDidBeginEditing...
		NSEnumerator *e = [new objectEnumerator];
		Search *s;
		BOOL autosave = [[NSUserDefaults standardUserDefaults] boolForKey:SolarSeekDefaultSaveSearches];
		while(s = [e nextObject]) {
			SearchSourceListElement *ssle = [[[SearchSourceListElement alloc] initWithSearch:s autosaves:autosave] autorelease];
			[_dictionaryElementsSearch setObject:ssle forKey:[s query]];
			[add addObject:ssle];
		}
		[_elementsSearch addElements:add];
	}
}

- (void)autoloadSearches {
	[NSThread detachNewThreadSelector:@selector(autoloadSearchesInBackground) toTarget:self withObject:nil];
}

- (void)autoloadSearchesInBackground {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	[NSThread setThreadPriority:0.1];
	NS_DURING
		[NSApp appendLogWithHeader:@"AUTOLOAD" HTML:@"Loading saved searches and browsings..." type:LogTypeLoad];
		NSFileManager *fm = [NSFileManager defaultManager];
		NSString *dir = [@"~/Library/Application Support/SolarSeek/" stringByExpandingTildeInPath];
		if([fm fileExistsAtPath:dir]) {
			NSDirectoryEnumerator *dirEnum = [fm enumeratorAtPath:dir];
			NSString *file;
			Search *s = nil;
			SourceListElement *sle = nil;
			while(file = [dirEnum nextObject]) {
				if([[file pathExtension] isEqualToString:@"plist"]) {
					if([[[file stringByDeletingPathExtension] pathExtension] isEqualToString:@"search"]) {
						[NSApp appendLogWithHeader:@"SEARCH" HTML:[NSString stringWithFormat:@"Loading %@.",[file stringByDeletingPathExtension]] type:LogTypeLoad|LogTypeLoud];
						s = [[[NSApp engine] searchesController] loadSearchFromFile:[dir stringByAppendingPathComponent:file]];
						sle = nil;
					}
					else if([[[file stringByDeletingPathExtension] pathExtension] isEqualToString:@"browse"]) {
						s = nil;
						[NSApp appendLogWithHeader:@"BROWSE" HTML:[NSString stringWithFormat:@"Loading %@.",[file stringByDeletingPathExtension]] type:LogTypeLoad|LogTypeLoud];
						Peer *p = [FriendsSourceListElement loadPeerAtPath:[dir stringByAppendingPathComponent:file]];
						sle = [self browsePeer:p select:NO saveImmediately:NO];
					}
				}
				if(s || sle) {
					[NSApp performSelectorOnMainThread:@selector(expandSourceListElement:) withObject:_elementsSearch waitUntilDone:NO];
					[NSApp performSelectorOnMainThread:@selector(updateSourceList) withObject:nil waitUntilDone:NO];
					s = nil; 
					sle = nil;
				}
			}
//			if(s || sle) {
//				[NSApp expandSourceListElement:_elementsSearch];
//			}
		}
	NS_HANDLER
		NSLog(@"%@",localException);
	NS_ENDHANDLER
	[pool release];
}

- (void)clear:(id)sender {
	[textSearch setStringValue:@""];
}

- (void)removeSearches:(NSArray *)old {
	int change=-1;
	if([old count]) {
		NSMutableArray *remove = [NSMutableArray arrayWithCapacity:[old count]];
		NSEnumerator *e = [old objectEnumerator];
		Search *s;
		while(s = [e nextObject]) {
			NSEnumerator *f = [[_elementsSearch children] objectEnumerator];
			SearchSourceListElement *ssle;
			while(ssle = [f nextObject]) {
				if([ssle search] == s) { 
//					if([ssle autosaves]) {
						if([[NSFileManager defaultManager] 
								removeFileAtPath:
									[[@"~/Library/Application Support/SolarSeek" 
											stringByAppendingPathComponent:[NSString 
												stringWithFormat:@"%@.search.plist",[s query]]] 
											stringByExpandingTildeInPath] handler:nil]==NO) {
//							NSLog(@"ERROR: Unable to automatically delete search %@",[s query]);
						}
//					}
					if([ssle isSelected])
						change=[[_elementsSearch children] indexOfObject:ssle];
					[_dictionaryElementsSearch removeObjectForKey:[s query]];
					[remove addObject:ssle];
					break;
				}
			}
		}
		[_elementsSearch removeElements:remove];
	}
	if(change > 0) {
		[NSApp updateSourceList];
		unsigned c = [[_elementsSearch children] count]; 
		if(c > 0) {
			if(change >= c)
				change = c-1;
			[NSApp selectSourceListElement:[[_elementsSearch children] objectAtIndex:change]];
		}
		else
			[NSApp selectSourceListElement:[self element]];
	}

}

- (SourceListElement *)elementForSearch:(Search *)search {
	return [_dictionaryElementsSearch objectForKey:[search query]];
}

- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
	if(returnCode == NSOKButton) {
		NSEnumerator *e = [[sheet filenames] objectEnumerator];
		NSString *filename;
		Search *search=nil;
		while(filename = [e nextObject]) {
			search = [[[NSApp engine] searchesController] loadSearchFromFile:filename];
		}
		
		if(search) {
			[NSApp expandSourceListElement:_elementsSearch];
			[NSApp selectSourceListElement:[self elementForSearch:search]];
		}
	}
}

- (IBAction)loadSearch:(id)sender {
	NSOpenPanel *op = [NSOpenPanel openPanel];
	[op setAllowsMultipleSelection:YES];
	[op setCanChooseDirectories:NO];
	[op setRequiredFileType:@"search.plist"];
	[op beginSheetForDirectory:nil file:nil modalForWindow:[NSApp keyWindow] modalDelegate:self  didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:nil];
}

- (IBAction)saveSearch:(id)sender {
	id o = [NSApp selectedSourceListElement];
	if([o respondsToSelector:@selector(save)]) {
		[o performSelector:@selector(save)];
	}
	else {
		Search *s = [o isKindOfClass:[SearchSourceListElement class]]?[(SearchSourceListElement *)o search]:nil;
		if(s==nil) 
			s = [[nibSearches selectedObjects] lastObject];
		if(s) {
			[(SearchSourceListElement *)[self elementForSearch:s] save];
		}
		else {
			SourceListElement *sle = o;
			[[NSAlert alertWithMessageText:@"Unable to save"
					defaultButton:@"OK" alternateButton:nil otherButton:nil
					informativeTextWithFormat:@"SolarSeek was unable to save because it does not know how to save the information about %@",[sle name]]
				beginSheetModalForWindow:[NSApp keyWindow]
				modalDelegate:nil didEndSelector:nil contextInfo:nil];
		}
	}
}

- (IBAction)select:(id)sender {
	[NSApp selectSourceListElement:[self element]];
}

- (IBAction)selectTextSearch:(id)sender {
	[[self window] makeFirstResponder:textSearch];
}

- (IBAction)selectAndSearchSelectedPeer:(id)sender {
	NSView *v = [[NSApp selectedSourceListElement] view];
	Peer *p = nil;
	if([v respondsToSelector:@selector(participantsSelected)])
		p = [[v performSelector:@selector(participantsSelected)] lastObject];
	if(p) {
		int idx = [menuSearchType indexOfItemWithTag:5];
		if(idx != -1)
			[menuSearchType selectItemAtIndex:idx];
			
		[textSpecificUser setStringValue:[p name]];
		[NSApp selectSourceListElement:[self element]];
	}
}

- (IBAction)selectAndSearchChats:(id)sender {
	int idx = [menuSearchType indexOfItemWithTag:3];
	if(idx != -1) {
		[menuSearchType selectItemAtIndex:idx];
			
		[NSApp selectSourceListElement:[self element]];
	}
}	

	
- (IBAction)makeKey:(id)sender {
	[NSApp selectSourceListElement:[self element]];
}

- (IBAction)moreSearch:(id)sender
{
	[[[NSApp engine] searchesController] searchForWords:[[[nibSearches selectedObjects] lastObject] query]];
}

- (IBAction)removeSearch:(id)sender
{
	[[[NSApp engine] searchesController] endSearch:[[nibSearches selectedObjects] lastObject]];
}

- (IBAction)startNetworkSearch:(id)sender {
	if([[textSearch stringValue] length] > 0) {
		Search *s = [(SearchesController *)[[NSApp engine] searchesController] searchForWords:[textSearch stringValue]];
		if(s) {
			[NSApp expandSourceListElement:_elementsSearch];
			[NSApp selectSourceListElement:[self elementForSearch:s]];
		}
	}
	[textSearch setStringValue:@""];
}

- (IBAction)startSearch:(id)sender
{
	[[self window] makeFirstResponder:textSpecificUser];
	Search *s = nil;
	if(([[textSearch stringValue] length] == 0 || [[textSearch stringValue] isEqual:@" "]) && [menuSearchType selectedTag] != 4) {
//			[[NSAlert alertWithMessageText:@"Enter search query" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"Before you can start your search, you must enter the words that you want to search for."]
//				beginSheetModalForWindow:[self window] modalDelegate:nil didEndSelector:nil contextInfo:nil];
			[[self window] makeFirstResponder:textSearch];
			return;
	}
	switch([menuSearchType selectedTag]) {
		case 5: {	//search someone specific
			if([[textSpecificUser stringValue] length] == 0) {
				[[NSAlert alertWithMessageText:@"Choose a user name" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"If you are searching a specific user, you must first choose that specific user's name."]
					beginSheetModalForWindow:[self window] modalDelegate:nil didEndSelector:nil contextInfo:nil];
				[[self window] makeFirstResponder:textSpecificUser];
				return;
			}
			s = [(SearchesController *)[[NSApp engine] searchesController] searchUser:[textSpecificUser stringValue] forWords:[textSearch stringValue]];
			break;	
		}
		case 2: {	//search friends
			NSArray *friends = [[FriendsSourceListElement friendsSourceListElementShared] participants];
			if([friends count]==0) {
				[[NSAlert alertWithMessageText:@"Empty Friend List" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"You must have at least one person in your friend list before you can do a friend search."]
					beginSheetModalForWindow:[self window] modalDelegate:nil didEndSelector:nil contextInfo:nil];
				[NSApp selectSourceListElement:[FriendsSourceListElement friendsSourceListElementShared]];
				return;
			}
			NSEnumerator *e = [friends objectEnumerator];
			Peer *p;
			NSString *query = [textSearch stringValue];
			while(p = [e nextObject]) {
				s = [[[NSApp engine] searchesController] searchUser:[p name] forWords:query];
			}
			break;
		}
		case 3: { //search all rooms.
			NSArray *chats = [[[[NSApp engine] chatsController] chatsInside] values];
			if([chats count]==0) {
				[[NSAlert alertWithMessageText:@"Not Inside Any Chats" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"You must be inside at least one chat before you can do a room search."]
					beginSheetModalForWindow:[self window] modalDelegate:nil didEndSelector:nil contextInfo:nil];
				[NSApp selectSourceListElement:[ChatsElement elementChatsRoot]];
				return;
			}
			Chat *chat;
			NSEnumerator *e = [chats objectEnumerator];
			NSMutableSet *peers = [NSMutableSet set];
			while(chat = [e nextObject]) {
				[peers unionSet:[[chat peersJoined] mutableSet]];
			}
			e = [peers objectEnumerator];
			Peer *p;
			NSString *query = [textSearch stringValue];
			while(p = [e nextObject]) {
				s = [[[NSApp engine] searchesController] searchUser:[p name] forWords:query];
			}
			break;
		}
		case 1:	//search the network.
			s = [(SearchesController *)[[NSApp engine] searchesController] searchForWords:[textSearch stringValue]];
			break;
		case 4: { //browse user 
			if([[textBrowseUser stringValue] length] == 0) {
				[[NSAlert alertWithMessageText:@"Choose a user name" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"If you are browsing a specific user, you must first choose that specific user's name."]
					beginSheetModalForWindow:[self window] modalDelegate:nil didEndSelector:nil contextInfo:nil];
				[[self window] makeFirstResponder:textSpecificUser];
				return;
			}
			[self browsePeer:[[NSApp engine] peerNamed:[textBrowseUser stringValue]] select:YES saveImmediately:YES];
			
			s = nil;
			break;
		}
		default:
			[[NSAlert alertWithMessageText:@"Unknown Type" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"This version does not know how to do a search with type %d.",[menuSearchType selectedTag]]
					beginSheetModalForWindow:[self window] modalDelegate:nil didEndSelector:nil contextInfo:nil];
			return;
	}
	if(s) {
		[NSApp expandSourceListElement:_elementsSearch];
		[NSApp selectSourceListElement:[self elementForSearch:s]];
	}
}

- (IBAction)selectNext:(id)sender {
	if([sender respondsToSelector:@selector(nextKeyView)])
		[[self window] performSelector:@selector(makeFirstResponder:) withObject:[sender nextKeyView] afterDelay:0.0];
	else 
		[[self window] performSelector:@selector(makeFirstResponder:) withObject:[sender tag]!=5?textSearch:textSpecificUser afterDelay:0.05];
}

- (IBAction)toggleAutosearch:(id)sender {
//	BOOL b = [[[NSUserDefaultsController sharedUserDefaultsController] valueForKeyPath:@"values.solarseekSaveSearches"] boolValue];

}
	
- (IBAction)viewResults:(id)sender
{
	[NSApp expandSourceListElement:_elementsSearch];
	[NSApp selectSourceListElement:[self elementForSearch:[[nibSearches selectedObjects] lastObject]]];
}

- (IBAction)browseSelectedPeers:(id)sender {
	Peer *p=nil;
	NSView *v = [[NSApp selectedSourceListElement] view];
	if([v respondsToSelector:@selector(participantsSelected)]) {
		p = [[v performSelector:@selector(participantsSelected)] lastObject];
	}
	else {
		p = [[nibControllerParticipants arrangedObjects] lastObject];
	}
	
	if(p) {
		[self browsePeer:p select:YES saveImmediately:YES];
	}
}


- (BrowseElement *)browsePeer:(Peer *)peer select:(BOOL)select saveImmediately:(BOOL)save {
	NSAssert(peer,@"Peer cannnot be nil when browsing.");
	NSMutableDictionary *elements = _dictionaryElementsBrowse;
	BrowseElement *be = [elements objectForKey:[peer name]];
	if(be == nil) {
		be = [[BrowseElement alloc] initWithView:nibBrowseView peer:peer browser:nibBrowser saveImmediately:save];
		[elements setObject:be forKey:[peer name]];
		[be release];
		[_elementsSearch addElement:be];
		[NSApp updateSourceList];
		if(save) 
			[[[NSApp engine] searchesController] browseUser:[peer name]];
	}
	[be setAutosaves:save];
	if(select) {
		[NSApp expandSourceListElement:[self element]];
		[NSApp selectSourceListElement:be];
	}
	return be;
}

- (void)endBrowseElement:(BrowseElement *)be {
	BOOL b = [be isSelected];
	int i = [[_elementsSearch children] indexOfObject:be];
	[_elementsSearch removeElement:be];
	[_dictionaryElementsBrowse removeObjectForKey:[[be peer] name]];
	[NSApp updateSourceList];
	if(b && i >= 0) {
		if([[_elementsSearch children] count] > 0) {
			if([[_elementsSearch children] count] <= i) {
				i = [[_elementsSearch children] count]-1;
			}
			[NSApp selectSourceListElement:[[_elementsSearch children] objectAtIndex:i]];
		}
		else [NSApp selectSourceListElement:_elementsSearch];
	}
	if([[NSFileManager defaultManager] 
			removeFileAtPath:
				[[@"~/Library/Application Support/SolarSeek" 
						stringByAppendingPathComponent:[NSString 
							stringWithFormat:@"%@.browse.plist",[be peer]]] 
						stringByExpandingTildeInPath] handler:nil]==NO) {
//		NSLog(@"ERROR: Unable to automatically delete browsing for %@.",[be peer]);
	}

}

@end
